package com.fusion.launchers.ui;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

import com.fusion.Subsystem;
import com.fusion.Version;
import com.fusion.core.ResourceSingelton;
import com.fusion.core.T;
import com.fusion.debug.LogUtil;
import com.fusion.launchers.OSInfo;
import com.fusion.launchers.SplashWindow;
import com.fusion.launchers.StartupProgressListener;
import com.stendahls.nif.util.SimpleTimer;

/**
 * @author SwethaShivakumar 
 * Project: fusion
 * Date: Mar 17, 2009
 * URL: http://fusionwiki.co.cc
 * Mail Me At: shiva@fusionproject.co.cc
 */
public class Main {
    private static final int STARTED_SIGNAL_PORT = 56345;
    private static StartupProgressListener splashWindow;

    public static void main(String[] args) {
    	LogUtil.debug("Starting Fusion Initiated...");
        try {
            System.out.println("Launching Fusion v"+ Version.VERSION+" build "+Version.BUILD_NUMBER);
            System.setProperty("fusion.build", ""+Version.BUILD_NUMBER);

            boolean allowMultipleInstances = argsContain(args, "/allowMultipleInstances") || new File("allowMultipleInstances").exists();
            boolean runMinimized = argsContain(args, "/min");
         // Modified to OpenUI when user try to Run Fusion Which is already Running By Shiva @ 2:15 AM 3/26/2009
            if (!allowMultipleInstances) checkIfAlreadyRunning(true); // !runMinimized); 

        	LogUtil.debug("Initiating Splash Screen...");

            StartupProgressListener r = null;
            if (!runMinimized) r = (StartupProgressListener)Class.forName("com.fusion.launchers.SplashWindow").newInstance();
            splashWindow = r;
            LogUtil.debug("Splash Loded...");
            SplashWindow.setStartupProgressPercent(4);
            splashWindow.updateProgress("Splash Loded...");
            String s = getSettingsFile();
            LogUtil.debug("Initiating Core...");
            for(int i=0;i<args.length;i++) if (!args[i].startsWith("/")) s = args[i];
            Subsystem core = initCore(s, (StartupProgressListener)r);
            if (core == null) return; //oops. core crashed. Error message has been displayd. just bail.

            LogUtil.debug("Core Initiated...");
            LogUtil.debug("Initiating Tray...");
            Subsystem tray = null;
            try {
                tray = initTrayIcon(core);
                LogUtil.debug("Tray Initiated...");
                OSInfo.setSupportsTrayIcon(true);
            } catch(Throwable t) {
                OSInfo.setSupportsTrayIcon(false);
            }

            if (OSInfo.supportsTrayIcon()) {
                if (!runMinimized) {
                	LogUtil.debug("Opening UI...");
                    ((Runnable)tray).run(); //open ui
                    SplashWindow.setStartupProgressPercent(100);
                    splashWindow.updateProgress("All Modules Loaded Successfully");
                    LogUtil.debug("UI opened...");
                    LogUtil.debug("Closing Splash Window...");
                    if (r != null) r.run(); //close splash window
                }

                if (!allowMultipleInstances) startStartSignalThread(tray);
            } else {
                initUI(core);
                if (r != null) r.run();
            }
        } catch (Throwable e) {
            try {
                new File("logs").mkdirs();
                PrintWriter writer = new PrintWriter("logs/crash.log");
                e.printStackTrace(writer);
                writer.close();
            } catch (FileNotFoundException e2) {
                e.printStackTrace();
            }
        }
        LogUtil.debug("Fusion Startup From Main Completed...");
        LogUtil.flush();
    }

	private static String getSettingsFile() {
		String s = "data/settings.xml"; 

		String home = System.getProperty("user.home"); 
		@SuppressWarnings("unused")
		String system = System.getProperty("os.name"); 
		if(OSInfo.isLinux()) {
			home = home+"/.fusion/"; 
		} else {
			home = "";
		}

    	return home+s; 
    }
    
    private static boolean argsContain(String[] args, String pattern) {
        if (args != null) for(String s : args) if (pattern.equalsIgnoreCase(s)) return true;
        return false;
    }

    private static void checkIfAlreadyRunning(boolean startUI) {
        try {
            Socket s = new Socket("127.0.0.1", STARTED_SIGNAL_PORT);
            System.out.println("Socket Connection Tried...For Running Instance On Port 56345");
            OutputStream o = s.getOutputStream();
            System.out.println("Got Socket OutputStream For Running Instance On Port 56345");
            o.write(startUI ? 1 : 0);
            System.out.println("Written an Integer Message"+(startUI ? 1 : 0)+"To Socket Connection For Running Instance On Port 56345");
            o.flush();
            Thread.sleep(1000);
            System.out.println("Program already running. Closing this program instance...");
            System.exit(0);
        } catch(Exception e) {
            System.out.println("Program does not seem to be running. Starting.");
        }
    }

    private static Thread signalThread;
    private static ServerSocket signalServerSocket;
    private static void startStartSignalThread(final Subsystem tray) {
        signalThread = new Thread(new Runnable() {
            public void run() {
                try {
                    signalServerSocket = new ServerSocket(STARTED_SIGNAL_PORT, 0, InetAddress.getByName("127.0.0.1"));
                    while(true) {
                        try {
                            Socket s = signalServerSocket.accept(); //connection is made on this port if user wants to open the ui
                            System.out.println("May be an Existance(Already Running) Test Request Came...Let Us Check...Ha..");
//                            if (signalThread == null) return;
                            int b = s.getInputStream().read();
                            System.out.println("Request received by accepting data, Data Read: "+b);
                            s.close();
                            if (b == 1) ((Runnable)tray).run(); //open ui
                        } catch(IOException e) {
                            if (signalThread == null) return;
                            e.printStackTrace();
                            try { Thread.sleep(10000); } catch(InterruptedException e1) {}
                        }
                    }
                } catch(IOException e) {
                    e.printStackTrace();
                }
                System.out.println("Exiting Run Method of: StartSignalThread...");
            }
        });
        signalThread.setDaemon(true);
        signalThread.start();
    }

    public static void stopStartSignalThread() {
        if (signalThread != null) {
            Thread t = signalThread;
            signalThread = null;
            try { signalServerSocket.close(); } catch(Exception e) {}
            try { t.join(); } catch(InterruptedException e) {}
        }
    }

    private static Subsystem initTrayIcon(Subsystem core) throws Throwable {
        try {
            if(T.t)T.info("Starting Java 6 tray icon...");
            Subsystem tray = (Subsystem)Class.forName("com.fusion.launchers.ui.Java6TrayIconSubsystem").newInstance();
            tray.init(ResourceSingelton.getRl(), core);
            return tray;
        } catch(Throwable e) {
        	if(T.t)T.debug("Unable to Initialise Java6 Tray Icon"+e);
        	System.err.println(e);
            if(T.t)T.warn("Java 6 tray icon not supported. Falling back to old code.");
            Subsystem tray = (Subsystem)Class.forName("com.fusion.launchers.ui.JDesktopTrayIconSubsystem").newInstance();
            tray.init(ResourceSingelton.getRl(), core);
            return tray;
        }
    }

    private static Subsystem initCore(String settings, StartupProgressListener l) {
        try {
            SimpleTimer s = new SimpleTimer();
            Subsystem core = (Subsystem)Class.forName("com.fusion.core.CoreSubsystem").newInstance();
            core.init(ResourceSingelton.getRl(), settings, l);
            if(T.t)T.info("" +
                    "Subsystem CORE started in "+s.getTime());
            return core;
        } catch(Throwable t) {
            reportError(t);
            System.err.println(t);
            return null;
        }
    }

    public static void reportError(Throwable t) {
        try {
            t.printStackTrace();
            //report error. Use reflection to init dialogs because we want NO references to UI stuff in this
            //class - we want this class to load fast (ie load minimal amount of classes)
            Object errorDialog = Class.forName("com.stendahls.ui.ErrorDialog").newInstance();
            Method m = errorDialog.getClass().getMethod("init", Throwable.class, boolean.class);
            m.invoke(errorDialog, t, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void initUI(Subsystem core) {
        try {
        	LogUtil.debug("Initialising UI");
            System.out.println("starting ui");
            SimpleTimer s = new SimpleTimer();
            Subsystem ui = (Subsystem)Class.forName("com.fusion.ui.UISubsystem").newInstance();
            ui.init(ResourceSingelton.getRl(), core);
            if(T.t)T.trace("Subsystem UI started in "+s.getTime());
            LogUtil.debug("UI Started IN "+ s.getTime());
        } catch(Exception t) {
            reportError(t);
        }
    }

	public static StartupProgressListener getSplashWindow() {
		return splashWindow;
	}
}
